package util;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.io.InterruptedIOException;

import javax.xml.bind.JAXBException;

/**
 * An {@link MBMessageChannelInterface} that interfaces with an input and output stream.
 * These streams are wrapped by a buffered stream and a data stream, with communication taking place of the modified UTF encoding.
 * @author Bitidork
 * @see DataOutputStream#writeUTF(String)
 * @see DataInputStream#readUTF()
 *
 */
public class MBMessageStreamInterface implements MBMessageChannelInterface {
	
	// %%%%%%%%%%%%%%%%%%%%%
	// Constants
	// %%%%%%%%%%%%%%%%%%%%% 
	
	/**
	 * The size of the buffer used to buffer the input and output streams of the socket.
	 */
	private static final int BUFFERED_STREAM_SIZE = 1024;
	
	// %%%%%%%%%%%%%%%%%%%%%%
	// Constructors
	// %%%%%%%%%%%%%%%%%%%%%%
	
	public MBMessageStreamInterface( final InputStream is, final OutputStream os ) {
		if ( is == null )
			throw new IllegalArgumentException("The supplied input stream was null");
		else if ( os == null )
			throw new IllegalArgumentException("The supplied output stream was null");
		
		// create the buffered streams
		BufferedInputStream bis = new BufferedInputStream( is, BUFFERED_STREAM_SIZE );
		BufferedOutputStream bos = new BufferedOutputStream( os, BUFFERED_STREAM_SIZE );
		
		// create the data streams
		dis = new DataInputStream( bis );
		dos = new DataOutputStream( bos );
	}
	
	// %%%%%%%%%%%%%%%%%%%%%%
	// Fields
	// %%%%%%%%%%%%%%%%%%%%%%
	
	/**
	 * The data input stream from the other entity.
	 * This is buffered and used only to read strings containing messages.
	 */
	private final DataInputStream dis;
	
	/**
	 * The data output stream to the other entity.
	 * This is buffered and used only to write strings containing messages.
	 */
	private final DataOutputStream dos;
	
	// %%%%%%%%%%%%%%%%%%%%%%
	// Reading/Writing
	// %%%%%%%%%%%%%%%%%%%%%%
	
	@Override
	final public MBMessage read( ) throws InterruptedIOException, JAXBException, IOException, InterruptedException {
		// read string from data input stream, may lead to IOException or timeout
		String str = dis.readUTF( );
		
		// unmarshal the message, duck exceptions
		return MBMessage.fromStringEx( str );
	}
	
	@Override
	final public void write( final MBMessage msg ) throws JAXBException, IOException, InterruptedException {
		// convert to string
		String str = msg.toStringEx( );
		
		// write to stream
		dos.writeUTF( str );
		
		// flush stream
		dos.flush( );
	}

	@Override
	public void close() {
		try {
			this.dos.close( );
			this.dis.close( );
		} catch (Exception e) {
			// best effort
		}
	}
}
